﻿using DotNetCommon.Accessors;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon.Extensions
{
    /// <summary>
    /// 通用扩展方法
    /// </summary>
    public static class GenericExtensions
    {
        /// <summary>
        /// 判断当前对象是否所属<seealso cref="Type"/>的默认值(<c>null</c>被认为是默认值)
        /// </summary>
        public static bool IsDefault<T>(this T @object) => EqualityComparer<T>.Default.Equals(@object, default);

        /// <summary>
        /// 返回一个未初始化的实例,注意: 未执行任何的初始化逻辑,比如 构造函数/字段初始化等，由于返回的对象不在一个正常的状态，请慎用。
        /// </summary>
        /// <remarks>参照：<see href="https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getuninitializedobject.aspx"/></remarks>
        /// <example>
        /// <code>
        /// class Person { public Person(int Id) { this.Id = Id; } public int Id { get; set; } = 5; public string Name { get; set; } = "小明"; }
        /// var person = ExpressionExtensions.GetUninitializedInstance&lt;Person&gt;();
        /// //输出: "0,null"
        /// Console.WriteLine($"{person.Id},{person.Name ?? "null"}");
        /// </code>
        /// </example>
        public static T GetUninitializedInstance<T>() => (T)FormatterServices.GetUninitializedObject(typeof(T));

        /// <summary>
        /// 获取符合条件的属性名集合
        /// </summary>
        /// <param name="object"></param>
        /// <param name="includePrivate">是否包含私有属性</param>
        /// <param name="inherit">是否包含继承属性</param>
        public static string[] GetPropertyNames<T>(this T @object, bool inherit = true, bool includePrivate = true)
        {
            if (@object is IDynamicMetaObjectProvider expando)
            {
                var dic = (IDictionary<string, object>)expando;
                return dic.Keys.ToArray();
            }

            return @object.GetType()
                .GetInstanceProperties(inherit, includePrivate)
                .Select(p => p.Name).ToArray();
        }

        /// <summary>
        /// 提供一个具有超时功能的锁
        /// </summary>
        public static Locker Lock<T>(this T obj, TimeSpan timeout) where T : class
        {
            var lockTaken = false;

            try
            {
                Monitor.TryEnter(obj, timeout, ref lockTaken);
                if (lockTaken) { return new Locker(obj); }
                throw new TimeoutException("Failed to acquire a lock within the timeout period of: " + timeout.ToString());
            }
            catch
            {
                if (lockTaken) { Monitor.Exit(obj); }
                throw;
            }
        }

        /// <summary>
        /// 具有超时功能的锁
        /// </summary>
        public struct Locker : IDisposable
        {
            private readonly object _obj;

            /// <summary>
            /// Returns an instance of <see cref="Locker"/>.
            /// </summary>
            /// <param name="obj">The <c>object</c> on which lock is taken.</param>
            internal Locker(object obj) => _obj = obj;

            /// <summary>
            /// Releases any locks taken by this instance.
            /// </summary>
            public void Dispose() => Monitor.Exit(_obj);
        }

        /// <summary>
        /// 将当前对象封住为一个完成态的Task <c>Task.FromResult(this)</c>
        /// </summary>
        [DebuggerStepThrough]
        public static Task<T> ToCompletedTask<T>(this T result) => Task.FromResult(result);

        /// <summary>
        /// Creates a ValueTask that's completed successfully with the specified <paramref name="result"/>.
        /// </summary>
        [DebuggerStepThrough]
        public static ValueTask<T> ToCompletedValueTask<T>(this T result) => new ValueTask<T>(result);

        /// <summary>
        /// 返回自身, obj=>obj, 可作为表达式解析的标记, 如:
        /// <code>
        /// i=>i.Children.Add(new Node{Id=2,Name="test"}.Eval())
        /// </code>
        /// </summary>
        /// <remarks>
        /// 应用实例: 在 ExpressionHelper.ReduceLambda() 中, 将默认对 Eval 前的部分进行求值简化
        /// </remarks>
        [DebuggerStepThrough]
        public static T Eval<T>(this T obj) => obj;

        #region SetMemberFluent 废弃
        ///// <summary>
        ///// 设置普通类的属性, 示例:
        ///// <code>
        ///// public class Person { prop DateTime CreateTime; prop DateTime UpdateTime; prop string Name}
        ///// 
        ///// var person = new Person();
        ///// person.SetFluent(i=>i.Name,"jack").SetFluent(i=>new {i.CreateTime,i.UpdateTime}, DateTime.Now);
        ///// </code>
        ///// </summary>
        //public static T SetMemberFluent<T, TValue>(this T obj, Expression<Func<T, TValue>> propSelect, TValue val) where T : class
        //{
        //    Ensure.NotNull(obj, nameof(obj));
        //    var names = ExpressionHelper.GetInitOrReturnPropNames(propSelect);
        //    var accessor = Accessor.Build<T>();
        //    foreach (var name in names) accessor[obj, name] = val;
        //    return obj;
        //}

        ///// <summary>
        ///// 设置普通类的属性, 示例:
        ///// <code>
        ///// public class Person { prop int Id; prop string Name}
        ///// 
        ///// var person = new Person();
        ///// person.SetFluent("Id",1).SetFluent("Name","jack");
        ///// </code>
        ///// </summary>
        //public static T SetMemberFluent<T>(this T obj, string propName, object val) where T : class
        //{
        //    Ensure.NotNull(obj, nameof(obj));
        //    var accessor = Accessor.Build<T>();
        //    accessor[obj, propName] = val;
        //    return obj;
        //}

        ///// <summary>
        ///// 设置普通类的属性, 示例:
        ///// <code>
        ///// public class Person { prop int Age1; prop int Age2; prop string Name}
        ///// 
        ///// var person = new Person{Age1=18,Age2=18,Name="tom"};
        ///// person.SetMemberFluent(i=>i.Name,i=>i+"2").SetMemberFluent(i=>new {i.Age1,i.Age2}, i=>i+1);
        ///// </code>
        ///// </summary>
        //public static T SetMemberFluent<T, TValue>(this T obj, Expression<Func<T, TValue>> propSelect, Func<TValue, TValue> setValue) where T : class
        //{
        //    Ensure.NotNull(obj, nameof(obj));
        //    var names = ExpressionHelper.GetInitOrReturnPropNames(propSelect);
        //    var accessor = Accessor.Build<T>();
        //    foreach (var name in names) accessor[obj, name] = setValue((TValue)accessor[obj, name]);
        //    return obj;
        //}
        #endregion
    }
}